#include <sys/types.h>

#if defined(ultrix) || defined(sun)
#include <sgtty.h>
#endif

#if defined(sun) || defined(sgi)
#   include <dirent.h>
#else
#   include <sys/dir.h>
#endif

#include <sys/stat.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <sys/param.h>
#include <pwd.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <netdb.h>
#include <fcntl.h>

#define SERVERPATH "/tmp"
#define PROTOCOLNAME "tcp"
#define MAXSIGNALS 11

extern int errno, select();
extern char *sys_errlist[];

/*---- signals ---------------------------------------------------------------*/

static struct signal_map {
	int code;
	int (*handler)();
} signal_map[MAXSIGNALS]= {
	SIGBUS, 0,
	SIGSEGV, 0,
	SIGSYS, 0,
	SIGPIPE, 0,
	SIGILL, 0,
	SIGQUIT, 0,
	SIGINT, 0,
	SIGWINCH, 0,
	SIGALRM, 0,
	SIGCHLD, 0,
	SIGURG, 0
};

static void sighandler(sig)
int sig;
{
	int i;

	for (i= 0; i < MAXSIGNALS; i++) {
		if (signal_map[i].code == sig) {
			(*signal_map[i].handler)(i);
			return;
		}
	}
}

void unix_signal(sig, handler)
int sig;
int (*handler)();
{
	if (signal_map[sig].handler != handler) {
		struct sigaction sigact;
		
		signal_map[sig].handler= handler;

		sigact.sa_handler= sighandler;
		sigemptyset(&sigact.sa_mask);
#if defined(SA_INTERRUPT)       /* SunOS */
		sigact.sa_flags= SA_INTERRUPT;
#else
		sigact.sa_flags= 0;
#endif
		if (sigaction(signal_map[sig].code, &sigact, NULL) < 0)
			perror("unix_signal: sigaction");
	}
}

char *unix_signame(sig)
int sig;
{
	int s= signal_map[sig].code;
#if defined(_AUX_SOURCE)
	static char buf[20];
	sprintf(buf, "signal %d", s);
	return buf;
#else
	extern char *sys_siglist[];
	return sys_siglist[s];
#endif
}

/*---- time ------------------------------------------------------------------*/

long unix_now()
{
	struct timeval t;
	gettimeofday(&t, 0);
	return t.tv_sec * 1000 + t.tv_usec / 1000;
}

int unix_setitimer(ms)
long ms;
{
	struct itimerval itv;
	itv.it_interval.tv_sec= itv.it_interval.tv_usec= 0;
	itv.it_value.tv_sec= itv.it_value.tv_usec= 0;
	if (ms >= 0) {
		itv.it_value.tv_sec= ms / 1000;
		itv.it_value.tv_usec= (ms % 1000) * 1000;
	}
	return setitimer(ITIMER_REAL, &itv, 0);
}

/*---- file descriptors ------------------------------------------------------*/

int unix_select(nfds, readready, writeready, timeout)
unsigned int nfds;
long timeout;
fd_set *readready, *writeready;
{
	int retcode;

	if (timeout >= 0) {
		struct timeval tv;
		tv.tv_sec= timeout / 1000;
		tv.tv_usec= (timeout % 1000) * 1000;
		retcode= select(nfds, readready, writeready, 0, &tv);
	} else {
		retcode= select(nfds, readready, writeready, 0, 0);
	}
	if (retcode == -1) {
		if (errno == EINTR) {
			errno= 0;  /* errno is not self reseting */
			return -2;
		}
		if (errno == EBADF)
			return -3;
		return -1;
	}
	return retcode;
}

int unix_nonblock(fd)
int fd;
{
	int val;
	
	if (fd < 0)
		return -1;

	if ((val= fcntl(fd, F_GETFL, 0)) < 0) {
		perror("unix_nonblock: F_GETFL");
		return val;
	}
#if 1 || defined(sun)
	val |= O_NDELAY;
#else
	val |= O_NONBLOCK;
#endif
	if ((val= fcntl(fd, F_SETFL, val)) < 0) {
		/*perror("unix_nonblock: F_SETFL");*/
		return val;
	}
	return 0;
}

int unix_ioctl(fd, code, vp, msg)
int fd, code;
void *vp;
char *msg;
{
	int rc;
	if (fd < 0)
		return -1;
	rc= ioctl(fd, code, vp);
	if (rc == -1)
		perror(msg);
	return rc;
}

/*---- directories -----------------------------------------------------------*/

char *unix_homedirectory(name)
char *name;
{
	static char path[MAXPATHLEN], mydir[MAXPATHLEN];
	struct passwd *pw;

	if (name) {
		pw= getpwnam(name);
		if (pw) {
			strncpy(path, pw->pw_dir, MAXPATHLEN);
			return path;
		}
	} else {
		if (mydir[0])
			return mydir;
		pw= getpwuid(getuid());
		if (pw) {
			strncpy(mydir, pw->pw_dir, MAXPATHLEN);
			return mydir;
		}
	}
	return 0;
}

void *unix_opendir(dir)
char *dir;
{
	struct stat finfo;

	if (stat(dir, &finfo) < 0)
		return 0;

	if (!S_ISDIR(finfo.st_mode))
		return 0;

	return (void*) opendir(dir);
}

#if defined(_POSIX_SOURCE)
/* Posix does not require that the d_ino field be present, and some
	systems do not provide it. */
#   define REAL_DIR_ENTRY(dp) 1
#else
#   define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
#endif /* _POSIX_SOURCE */

char *unix_getdirentry(dirp)
DIR *dirp;
{
#if defined(sun) || defined(sgi)
	struct dirent *dp;
#else
	struct direct *dp;
#endif sun

	if (dirp) {
		for (;;) {
			dp= readdir(dirp);
			if (dp == 0)
				return 0;
			if (REAL_DIR_ENTRY(dp))
				return dp->d_name;
		}
	}
	return 0;
}

/*---- files -----------------------------------------------------------------*/

int unix_filestat(path, id, size, flags, modtime)
char *path;
u_long *size, *modtime, *id, *flags;
{
	struct stat statbuf;

	if (path != 0 && stat(path, &statbuf) >= 0) {
		if (id)
			*id= (statbuf.st_dev << 24) + statbuf.st_ino;
		if (size)
			*size= statbuf.st_size;
		if (modtime)
			*modtime= statbuf.st_mtime;
		if (flags) {
			if (statbuf.st_mode & ((S_IEXEC)|(S_IEXEC>>3)|(S_IEXEC>>6)))
				*flags|= 1;
			if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
				*flags|= 2;
			if ((statbuf.st_mode & S_IFMT) != S_IFREG
							&& ((statbuf.st_mode & S_IFMT) != S_IFDIR))
				*flags|= 4;
		}
		return 0;
	}
	return 1;
}

/*---- files -----------------------------------------------------------------*/

int unix_waitchild()
{
	union wait status;
	return wait3(&status, WNOHANG, 0);
}

/*---- RPC -------------------------------------------------------------------*/

int unix_TcpConnect(hostname, servicename)
char *hostname, *servicename;
{
	int sock;
	struct sockaddr_in server;
	struct hostent *host_ptr;
	struct servent *sp;

	if ((sp= getservbyname(servicename, PROTOCOLNAME)) == NULL) {
		Error("TcpConnect", "no service \"%s\" with protocol \"%s\"\n",
												servicename, PROTOCOLNAME);
		return -1;
	}

	if ((host_ptr= gethostbyname(hostname)) == 0) {
		Error("TcpConnect", "unknown host %s\n", hostname);
		return -1;
	}

	/* Check the address type for an internet host */
	if (host_ptr->h_addrtype != AF_INET) {
		Error("TcpConnect", "%s is not an internet host\n", hostname);
		return -1;
	}

	bzero(&server, sizeof(server));
	bcopy(host_ptr->h_addr, &server.sin_addr, host_ptr->h_length);
	server.sin_family = host_ptr->h_addrtype;
	server.sin_port= htons(sp->s_port);

	/* Create socket */
	if ((sock= socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		SysError("ConnectTcp", "socket");
		return -1;
	}
		
	if (connect(sock, (struct sockaddr*) &server, sizeof(server)) < 0) {
		/* SysError("ConnectTcp", "connect"); */
		return -1;
	}
	return sock;
}

int unix_UnixConnect(name)
char *name;
{
	int sock;
	char buf[100];
	struct sockaddr_un unserver;

	sprintf(buf, "%s/%s", SERVERPATH, name);

	unserver.sun_family= AF_UNIX;
	strcpy(unserver.sun_path, buf);

	/* open socket */
	if ((sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
		SysError("UnixConnect", "socket");
		return -1;
	}
		
	if (connect(sock, (struct sockaddr*) &unserver, strlen(unserver.sun_path)+2) < 0) {
		/* SysError("UnixConnect", "connect"); */
		close(sock);
		return -1;
	}
	return sock;
}

int unix_TcpService(servicename)
char *servicename;
{
	int retry, sock;
	struct sockaddr_in inserver;
	struct servent *sp;

	if ((sp= getservbyname(servicename, PROTOCOLNAME)) == NULL) {
		Warning("TcpService", "no service \"%s\" with protocol \"%s\"\n",
													servicename, PROTOCOLNAME);
		return -1;
	}

	/* Create tcp socket */
	if ((sock= socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		SysError("TcpService", "socket");
		return -1;
	}
		
	bzero(&inserver, sizeof(inserver));
	inserver.sin_family= AF_INET;
	inserver.sin_addr.s_addr= htonl(INADDR_ANY);
	inserver.sin_port= htonl(sp->s_port);

	/* bind socket */
	for (retry= 20; bind(sock, (struct sockaddr*) &inserver, sizeof(inserver)); retry--) {
		if (retry <= 0) {
			SysError("TcpService", "bind");
			return -1;
		}
		sleep(10);
	}

	/* Start accepting connections */
	if (listen (sock, 5)) {
		SysError("TcpService", "listen");
		return -1;
	}

	return sock;
}

/* returns socket fd or -1 */
int unix_UnixService(servername)
char *servername;
{
	struct sockaddr_un unserver;
	int sock, oldumask;

	bzero(&unserver, sizeof(unserver));
	unserver.sun_family = AF_UNIX;

	/* assure that socket directory exists */
	oldumask= umask(0);
	mkdir(SERVERPATH, 0777);
	umask(oldumask);
	sprintf(unserver.sun_path, "%s/%s", SERVERPATH, servername);

	/* remove old socket */
	unlink(unserver.sun_path);

	/* Create socket */
	if ((sock= socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
		SysError("UnixService", "socket");
		return -1;
	}

	if (bind(sock, (struct sockaddr*) &unserver, strlen(unserver.sun_path)+2)) {
		SysError("UnixService", "bind");
		return -1;
	}

	/* Start accepting connections */
	if (listen(sock, 5)) {
		SysError("UnixService", "listen");
		return -1;
	}

	return sock;
}

